1//////////////////////////////////////////////////////////////////////
   2// LibFile: beziers.scad
   3//   Bezier functions and modules.
   4//   To use, add the following lines to the beginning of your file:
   5//   ```
   6//   include <BOSL/constants.scad>
   7//   use <BOSL/beziers.scad>
   8//   ```
   9//////////////////////////////////////////////////////////////////////
  10
  11/*
  12BSD 2-Clause License
  13
  14Copyright (c) 2017, Revar Desmera
  15All rights reserved.
  16
  17Redistribution and use in source and binary forms, with or without
  18modification, are permitted provided that the following conditions are met:
  19
  20* Redistributions of source code must retain the above copyright notice, this
  21	list of conditions and the following disclaimer.
  22
  23* Redistributions in binary form must reproduce the above copyright notice,
  24	this list of conditions and the following disclaimer in the documentation
  25	and/or other materials provided with the distribution.
  26
  27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  28AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  29IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  30DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  31FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  32DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  33SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  34CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  35OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  36OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  37*/
  38
  39
  40include <constants.scad>
  41use <math.scad>
  42use <paths.scad>
  43use <transforms.scad>
  44
  45
  46// Section: Terminology
  47//   **Polyline**: A series of points joined by straight line segements.
  48//   
  49//   **Bezier Curve**: A mathematical curve that joins two endpoints, following a curve determined by one or more control points.
  50//   
  51//   **Endpoint**: A point that is on the end of a bezier segment.  This point lies on the bezier curve.
  52//   
  53//   **Control Point**: A point that influences the shape of the curve that connects two endpoints.  This is often *NOT* on the bezier curve.
  54//   
  55//   **Degree**: The number of control points, plus one endpoint, needed to specify a bezier segment.  Most beziers are cubic (degree 3).
  56//   
  57//   **Bezier Segment**: A list consisting of an endpoint, one or more control points, and a final endpoint.  The number of control points is one less than the degree of the bezier.  A cubic (degree 3) bezier segment looks something like:
  58//       `[endpt1, cp1, cp2, endpt2]`
  59//   
  60//   **Bezier Path**: A list of bezier segments flattened out into a list of points, where each segment shares the endpoint of the previous segment as a start point. A cubic Bezier Path looks something like:
  61//       `[endpt1, cp1, cp2, endpt2, cp3, cp4, endpt3]`
  62//   **NOTE**: A bezier path is *NOT* a polyline.  It is only the points and controls used to define the curve.
  63//   
  64//   **Bezier Patch**: A surface defining grid of (N+1) by (N+1) bezier points.  If a Bezier Segment defines a curved line, a Bezier Patch defines a curved surface.
  65//   
  66//   **Bezier Surface**: A surface defined by a list of one or more bezier patches.
  67//   
  68//   **Spline Steps**: The number of straight-line segments to split a bezier segment into, to approximate the bezier curve.  The more spline steps, the closer the approximation will be to the curve, but the slower it will be to generate.  Usually defaults to 16.
  69
  70
  71// Section: Segment Functions
  72
  73// Function: bez_point()
  74// Usage:
  75//   bez_point(curve, u)
  76// Description:
  77//   Formula to calculate points on a bezier curve.  The degree of
  78//   the curve, N, is one less than the number of points in `curve`.
  79// Arguments:
  80//   curve = The list of endpoints and control points for this bezier segment.
  81//   u = The proportion of the way along the curve to find the point of.  0<=`u`<=1
  82// Example(2D): Quadratic (Degree 2) Bezier.
  83//   bez = [[0,0], [30,30], [80,0]];
  84//   trace_bezier(bez, N=len(bez)-1);
  85//   translate(bez_point(bez, 0.3)) color("red") sphere(1);
  86// Example(2D): Cubic (Degree 3) Bezier
  87//   bez = [[0,0], [5,35], [60,-25], [80,0]];
  88//   trace_bezier(bez, N=len(bez)-1);
  89//   translate(bez_point(bez, 0.4)) color("red") sphere(1);
  90// Example(2D): Degree 4 Bezier.
  91//   bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]];
  92//   trace_bezier(bez, N=len(bez)-1);
  93//   translate(bez_point(bez, 0.8)) color("red") sphere(1);
  94function bez_point(curve,u)=
  95	(len(curve) <= 1) ?
  96		curve[0] :
  97		bez_point(
  98			[for(i=[0:len(curve)-2]) curve[i]*(1-u)+curve[i+1]*u],
  99			u
 100		);
 101
 102// Function: bez_tangent()
 103// Usage:
 104//   bez_tangent(curve, u)
 105// Description:
 106//   Calculate the tangent vector at a point on a bezier curve.
 107// Arguments:
 108//   curve = The list of endpoints and control points for this bezier segment.
 109//   u = The proportion of the way along the curve to find the point of.  0<=`u`<=1
 110// Example(2D): Cubic (degree 3) bezier tangent
 111//   bez = [[0,0], [5,35], [60,-25], [80,0]];
 112//   point = bez_point(bez, 0.2);
 113//   tangent = bez_tangent(bez, 0.2) * 10;
 114//   trace_bezier(bez, N=len(bez)-1);
 115//   color("red") translate(point) extrude_from_to(-tangent, tangent) circle(d=.5);
 116function bez_tangent(curve, u) = (
 117	len(curve) <= 2
 118		? normalize(u * (curve[1] - curve[0]))
 119		: bez_tangent([
 120			for(i=[0:len(curve)-2])
 121				curve[i] * (1 - u) +
 122				curve[i + 1] * u
 123		], u)
 124);
 125
 126// Function: bezier_segment_closest_point()
 127// Usage:
 128//   bezier_segment_closest_point(bezier,pt)
 129// Description:
 130//   Finds the closest part of the given bezier segment to point `pt`.
 131//   The degree of the curve, N, is one less than the number of points in `curve`.
 132//   Returns `u` for the shortest position on the bezier segment to the given point `pt`.
 133// Arguments:
 134//   curve = The list of endpoints and control points for this bezier segment.
 135//   pt = The point to find the closest curve point to.
 136//   max_err = The maximum allowed error when approximating the closest approach.
 137// Example(2D):
 138//   pt = [40,15];
 139//   bez = [[0,0], [20,40], [60,-25], [80,0]];
 140//   u = bezier_segment_closest_point(bez, pt);
 141//   trace_bezier(bez, N=len(bez)-1);
 142//   color("red") translate(pt) sphere(r=1);
 143//   color("blue") translate(bez_point(bez,u)) sphere(r=1);
 144function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = 
 145	let(
 146		steps = len(curve)*3,
 147		path = [for (i=[0:1:steps]) let(v=(end_u-u)*(i/steps)+u) [v, bez_point(curve, v)]],
 148		bracketed = concat([path[0]], path, [path[len(path)-1]]),
 149		minima_ranges = [
 150			for (i = [1:1:len(bracketed)-1]) let(
 151				d1=norm(bracketed[i  ][1]-pt),
 152				d2=norm(bracketed[i+1][1]-pt),
 153				d3=norm(bracketed[i+2][1]-pt)
 154			) if(d2<=d1 && d2<=d3) [
 155				bracketed[i][0], bracketed[i+2][0]
 156			]
 157		]
 158	) len(minima_ranges)>1? (
 159		let(
 160			min_us = [
 161				for (minima = minima_ranges)
 162					bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y)
 163			],
 164			dists = [for (v=min_us) norm(bez_point(curve,v)-pt)],
 165			min_i = min_index(dists)
 166		) min_us[min_i]
 167	) : let(
 168		minima = minima_ranges[0],
 169		p1 = bez_point(curve, minima.x),
 170		p2 = bez_point(curve, minima.y)
 171	) norm(p1-p2)<max_err? mean(minima) :
 172	bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y);
 173
 174
 175
 176// Function: bezier_segment_length()
 177// Usage:
 178//   bezier_segment_length(curve, [start_u], [end_u], [max_deflect]);
 179// Description:
 180//   Approximates the length of the bezier segment between start_u and end_u.
 181// Arguments:
 182//   curve = The list of endpoints and control points for this bezier segment.
 183//   start_u = The proportion of the way along the curve to start measuring from.  Between 0 and 1.
 184//   end_u = The proportion of the way along the curve to end measuring at.  Between 0 and 1.  Greater than start_u.
 185//   max_deflect = The largest amount of deflection from the true curve to allow for approximation.
 186// Example:
 187//   bez = [[0,0], [5,35], [60,-25], [80,0]];
 188//   echo(bezier_segment_length(bez));
 189function bezier_segment_length(curve, start_u=0, end_u=1, max_deflect=0.01) =
 190	let(
 191		segs = len(curve) * 2,
 192		path = [
 193			for (i=[0:1:segs])
 194			let(u=lerp(start_u, end_u, i/segs))
 195			bez_point(curve,u)
 196		],
 197		defl = max([
 198			for (i=[0:1:len(path)-3]) let(
 199				mp = (path[i] + path[i+2]) / 2
 200			) norm(path[i+1] - mp)
 201		]),
 202		mid_u = lerp(start_u, end_u, 0.5)
 203	)
 204	defl <= max_deflect? path_length(path) :
 205	sum([
 206		for (i=[0:1:segs-1]) let(
 207			su = lerp(start_u, end_u, i/segs),
 208			eu = lerp(start_u, end_u, (i+1)/segs)
 209		) bezier_segment_length(curve, su, eu, max_deflect)
 210	]);
 211
 212
 213
 214// Function: fillet3pts()
 215// Usage:
 216//   fillet3pts(p0, p1, p2, r);
 217// Description:
 218//   Takes three points, defining two line segments, and works out the
 219//   cubic (degree 3) bezier segment (and surrounding control points)
 220//   needed to approximate a rounding of the corner with radius `r`.
 221//   If there isn't room for a radius `r` rounding, uses the largest
 222//   radius that will fit.  Returns [cp1, endpt1, cp2, cp3, endpt2, cp4]
 223// Arguments:
 224//   p0 = The starting point.
 225//   p1 = The middle point.
 226//   p2 = The ending point.
 227//   r = The radius of the fillet/rounding.
 228//   maxerr = Max amount bezier curve should diverge from actual radius curve.  Default: 0.1
 229// Example(2D):
 230//   p0 = [40, 0];
 231//   p1 = [0, 0];
 232//   p2 = [30, 30];
 233//   trace_polyline([p0,p1,p2], showpts=true, size=0.5, color="green");
 234//   fbez = fillet3pts(p0,p1,p2, 10);
 235//   trace_bezier(slice(fbez, 1, -2), size=1);
 236function fillet3pts(p0, p1, p2, r, maxerr=0.1, w=0.5, dw=0.25) = let(
 237		v0 = normalize(p0-p1),
 238		v1 = normalize(p2-p1),
 239		midv = normalize((v0+v1)/2),
 240		a = vector_angle(v0,v1),
 241		tanr = min(r/tan(a/2), norm(p0-p1)*0.99, norm(p2-p1)*0.99),
 242		tp0 = p1+v0*tanr,
 243		tp1 = p1+v1*tanr,
 244		cp = p1 + midv * tanr / cos(a/2),
 245		cp0 = lerp(tp0, p1, w),
 246		cp1 = lerp(tp1, p1, w),
 247		cpr = norm(cp-tp0),
 248		bp = bez_point([tp0, cp0, cp1, tp1], 0.5),
 249		tdist = norm(cp-bp)
 250	) (abs(tdist-cpr) <= maxerr)? [tp0, tp0, cp0, cp1, tp1, tp1] :
 251		(tdist<cpr)? fillet3pts(p0, p1, p2, r, maxerr=maxerr, w=w+dw, dw=dw/2) :
 252		fillet3pts(p0, p1, p2, r, maxerr=maxerr, w=w-dw, dw=dw/2);
 253
 254
 255
 256// Section: Path Functions
 257
 258
 259// Function: bezier_path_point()
 260// Usage:
 261//   bezier_path_point(path, seg, u, [N])
 262// Description: Returns the coordinates of bezier path segment `seg` at position `u`.
 263// Arguments:
 264//   path = A bezier path to approximate.
 265//   seg = Segment number along the path.  Each segment is N points long.
 266//   u = The proportion of the way along the segment to find the point of.  0<=`u`<=1
 267//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 268function bezier_path_point(path, seg, u, N=3) = bez_point(select(path,seg*N,(seg+1)*N), u);
 269
 270
 271
 272// Function: bezier_path_closest_point()
 273// Usage:
 274//   bezier_path_closest_point(bezier,pt)
 275// Description:
 276//   Finds the closest part of the given bezier path to point `pt`.
 277//   Returns [segnum, u] for the closest position on the bezier path to the given point `pt`.
 278// Arguments:
 279//   path = A bezier path to approximate.
 280//   pt = The point to find the closest curve point to.
 281//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 282//   max_err = The maximum allowed error when approximating the closest approach.
 283// Example(2D):
 284//   pt = [100,0];
 285//   bez = [[0,0], [20,40], [60,-25], [80,0], [100,25], [140,25], [160,0]];
 286//   pos = bezier_path_closest_point(bez, pt);
 287//   xy = bezier_path_point(bez,pos[0],pos[1]);
 288//   trace_bezier(bez, N=3);
 289//   color("red") translate(pt) sphere(r=1);
 290//   color("blue") translate(xy) sphere(r=1);
 291function bezier_path_closest_point(path, pt, N=3, max_err=0.01, seg=0, min_seg=undef, min_u=undef, min_dist=undef) =
 292	let(curve = select(path,seg*N,(seg+1)*N))
 293	(seg*N+1 >= len(path))? (
 294		let(curve = select(path, min_seg*N, (min_seg+1)*N))
 295		[min_seg, bezier_segment_closest_point(curve, pt, max_err=max_err)]
 296	) : (
 297		let(
 298			curve = select(path,seg*N,(seg+1)*N),
 299			u = bezier_segment_closest_point(curve, pt, max_err=0.05),
 300			dist = norm(bez_point(curve, u)-pt),
 301			mseg = (min_dist==undef || dist<min_dist)? seg : min_seg,
 302			mdist = (min_dist==undef || dist<min_dist)? dist : min_dist,
 303			mu = (min_dist==undef || dist<min_dist)? u : min_u
 304		)
 305		bezier_path_closest_point(path, pt, N, max_err, seg+1, mseg, mu, mdist)
 306	);
 307
 308
 309
 310// Function: bezier_path_length()
 311// Usage:
 312//   bezier_path_length(path, [N], [max_deflect]);
 313// Description:
 314//   Approximates the length of the bezier path.
 315// Arguments:
 316//   path = A bezier path to approximate.
 317//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 318//   max_deflect = The largest amount of deflection from the true curve to allow for approximation.
 319function bezier_path_length(path, N=3, max_deflect=0.001) =
 320	sum([
 321		for (seg=[0:(len(path)-1)/N-1]) (
 322			bezier_segment_length(
 323				select(path, seg*N, (seg+1)*N),
 324				max_deflect=max_deflect
 325			)
 326		)
 327	]);
 328
 329
 330
 331// Function: bezier_polyline()
 332// Usage:
 333//   bezier_polyline(bezier, [splinesteps], [N])
 334// Description:
 335//   Takes a bezier path and converts it into a polyline.
 336// Arguments:
 337//   bezier = A bezier path to approximate.
 338//   splinesteps = Number of straight lines to split each bezier segment into. default=16
 339//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 340// Example(2D):
 341//   bez = [
 342//       [0,0], [-5,30],
 343//       [20,60], [50,50], [110,30],
 344//       [60,25], [70,0], [80,-25],
 345//       [80,-50], [50,-50]
 346//   ];
 347//   trace_polyline(bez, size=1, N=3, showpts=true);
 348//   trace_polyline(bezier_polyline(bez, N=3), size=3);
 349function bezier_polyline(bezier, splinesteps=16, N=3) = let(
 350		segs = (len(bezier)-1)/N
 351	) concat(
 352		[for (seg = [0:segs-1], i = [0:splinesteps-1]) bezier_path_point(bezier, seg, i/splinesteps, N=N)],
 353		[bezier_path_point(bezier, segs-1, 1, N=N)]
 354	);
 355
 356
 357
 358// Function: fillet_path()
 359// Usage:
 360//   fillet_path(pts, fillet, [maxerr]);
 361// Description:
 362//   Takes a 3D polyline path and fillets the corners, returning a 3d cubic (degree 3) bezier path.
 363// Arguments:
 364//   pts = 3D Polyline path to fillet.
 365//   fillet = The radius to fillet/round the polyline corners by.
 366//   maxerr = Max amount bezier curve should diverge from actual radius curve.  Default: 0.1
 367// Example(2D):
 368//   pline = [[40,0], [0,0], [35,35], [0,70], [-10,60], [-5,55], [0,60]];
 369//   bez = fillet_path(pline, 10);
 370//   trace_polyline(pline, showpts=true, size=0.5, color="green");
 371//   trace_bezier(bez, size=1);
 372function fillet_path(pts, fillet, maxerr=0.1) = concat(
 373	[pts[0], pts[0]],
 374	(len(pts) < 3)? [] : [
 375		for (p = [1 : len(pts)-2]) let(
 376			p1 = pts[p],
 377			p0 = (pts[p-1]+p1)/2,
 378			p2 = (pts[p+1]+p1)/2
 379		) for (pt = fillet3pts(p0, p1, p2, fillet, maxerr=maxerr)) pt
 380	],
 381	[pts[len(pts)-1], pts[len(pts)-1]]
 382);
 383
 384
 385// Function: bezier_close_to_axis()
 386// Usage:
 387//   bezier_close_to_axis(bezier, [N], [axis]);
 388// Description:
 389//   Takes a 2D bezier path and closes it to the specified axis.
 390// Arguments:
 391//   bezier = The 2D bezier path to close to the axis.
 392//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 393//   axis = The axis to close to, "X", or "Y".  Default: "X"
 394// Example(2D):
 395//   bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
 396//   closed = bezier_close_to_axis(bez);
 397//   trace_bezier(closed, size=1);
 398// Example(2D):
 399//   bez = [[30,50], [10,40], [50,10], [30,0], [10, -10], [10,-30], [20,-50]];
 400//   closed = bezier_close_to_axis(bez, axis="Y");
 401//   trace_bezier(closed, size=1);
 402function bezier_close_to_axis(bezier, N=3, axis="X") =
 403	let(
 404		bezend = len(bezier)-1,
 405		sp = bezier[0],
 406		ep = bezier[bezend]
 407	) (axis=="X")? concat(
 408		[for (i=[0:N-1]) lerp([sp.x,0], sp, i/N)],
 409		bezier,
 410		[for (i=[1:N]) lerp(ep, [ep.x,0], i/N)],
 411		[for (i=[1:N]) lerp([ep.x,0], [sp.x,0], i/N)]
 412	) : (axis=="Y")? concat(
 413		[for (i=[0:N-1]) lerp([0,sp.y], sp, i/N)],
 414		bezier,
 415		[for (i=[1:N]) lerp(ep, [0,ep.y], i/N)],
 416		[for (i=[1:N]) lerp([0,ep.y], [0,sp.y], i/N)]
 417	) : (
 418		assert_in_list("axis", axis, ["X","Y"])
 419	);
 420
 421
 422// Function: bezier_offset()
 423// Usage:
 424//   bezier_offset(inset, bezier, [N], [axis]);
 425// Description:
 426//   Takes a 2D bezier path and closes it with a matching reversed path that is closer to the given axis by distance `inset`.
 427// Arguments:
 428//   inset = Amount to lower second path by.
 429//   bezier = The 2D bezier path.
 430//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 431//   axis = The axis to offset towards, "X", or "Y".  Default: "X"
 432// Example(2D):
 433//   bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
 434//   closed = bezier_offset(5, bez);
 435//   trace_bezier(closed, size=1);
 436// Example(2D):
 437//   bez = [[30,50], [10,40], [50,10], [30,0], [10, -10], [10,-30], [20,-50]];
 438//   closed = bezier_offset(5, bez, axis="Y");
 439//   trace_bezier(closed, size=1);
 440function bezier_offset(inset, bezier, N=3, axis="X") =
 441	let(
 442		backbez = reverse([ for (pt = bezier) pt-(axis=="X"? [0,inset] : [inset,0]) ]),
 443		bezend = len(bezier)-1
 444	) concat(
 445		bezier,
 446		[for (i=[1:N-1]) lerp(bezier[bezend], backbez[0], i/N)],
 447		backbez,
 448		[for (i=[1:N]) lerp(backbez[bezend], bezier[0], i/N)]
 449	);
 450
 451
 452
 453// Section: Path Modules
 454
 455
 456// Module: bezier_polygon()
 457// Usage:
 458//   bezier_polygon(bezier, [splinesteps], [N]) {
 459// Description:
 460//   Takes a closed 2D bezier path, and creates a 2D polygon from it.
 461// Arguments:
 462//   bezier = The closed bezier path to make into a polygon.
 463//   splinesteps = Number of straight lines to split each bezier segment into. default=16
 464//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 465// Example(2D):
 466//   bez = [
 467//       [0,0], [-5,30],
 468//       [20,60], [50,50], [110,30],
 469//       [60,25], [70,0], [80,-25],
 470//       [80,-50], [50,-50], [30,-50],
 471//       [5,-30], [0,0]
 472//   ];
 473//   trace_bezier(bez, N=3, size=3);
 474//   linear_extrude(height=0.1) bezier_polygon(bez, N=3);
 475module bezier_polygon(bezier, splinesteps=16, N=3) {
 476	polypoints=bezier_polyline(bezier, splinesteps, N);
 477	polygon(points=slice(polypoints, 0, -1));
 478}
 479
 480
 481// Module: linear_extrude_bezier()
 482// Usage:
 483//   linear_extrude_bezier(bezier, height, [splinesteps], [N], [center], [convexity], [twist], [slices], [scale], [orient], [align]);
 484// Description:
 485//   Takes a closed 2D bezier path, centered on the XY plane, and
 486//   extrudes it linearly upwards, forming a solid.
 487// Arguments:
 488//   bezier = Array of 2D points of a bezier path, to be extruded.
 489//   splinesteps = Number of steps to divide each bezier segment into. default=16
 490//   N = The degree of the bezier curves.  Cubic beziers have N=3.  Default: 3
 491//   convexity = max number of walls a line could pass through, for preview.  default=10
 492//   twist = Angle in degrees to twist over the length of extrusion.  default=0
 493//   scale = Relative size of top of extrusion to the bottom.  default=1.0
 494//   slices = Number of vertical slices to use for twisted extrusion.  default=20
 495//   center = If true, the extruded solid is centered vertically at z=0.
 496//   orient = Orientation of the extrusion.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
 497//   align = Alignment of the extrusion.  Use the `V_` constants from `constants.scad`.  Default: `ALIGN_POS`.
 498// Example:
 499//   bez = [
 500//       [-10,   0],  [-15,  -5],
 501//       [ -5, -10],  [  0, -10],  [ 5, -10],
 502//       [ 10,  -5],  [ 15,   0],  [10,   5],
 503//       [  5,  10],  [  0,  10],  [-5,  10],
 504//       [ 25, -15],  [-10,   0]
 505//   ];
 506//   linear_extrude_bezier(bez, height=20, splinesteps=32);
 507module linear_extrude_bezier(bezier, height=100, splinesteps=16, N=3, center=undef, convexity=undef, twist=undef, slices=undef, scale=undef, orient=ORIENT_Z, align=ALIGN_POS) {
 508	maxx = max([for (pt = bezier) abs(pt[0])]);
 509	maxy = max([for (pt = bezier) abs(pt[1])]);
 510	orient_and_align([maxx*2,maxy*2,height], orient, align) {
 511		linear_extrude(height=height, center=true, convexity=convexity, twist=twist, slices=slices, scale=scale) {
 512			bezier_polygon(bezier, splinesteps=splinesteps, N=N);
 513		}
 514	}
 515}
 516
 517
 518// Module: revolve_bezier()
 519// Usage:
 520//   revolve_bezier(bezier, [splinesteps], [N], [convexity], [angle], [orient], [align])
 521// Description:
 522//   Takes a closed 2D bezier and rotates it around the X axis, forming a solid.
 523// Arguments:
 524//   bezier = array of 2D points for the bezier path to rotate.
 525//   splinesteps = number of segments to divide each bezier segment into. default=16
 526//   N = number of points in each bezier segment.  default=3 (cubic)
 527//   convexity = max number of walls a line could pass through, for preview.  default=10
 528//   angle = Degrees of sweep to make.  Default: 360
 529//   orient = Orientation of the extrusion.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_X`.
 530//   align = Alignment of the extrusion.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
 531// Example(FlatSpin):
 532//   path = [
 533//     [  0, 10], [ 50,  0], [ 50, 40],
 534//     [ 95, 40], [100, 40], [100, 45],
 535//     [ 95, 45], [ 66, 45], [  0, 20],
 536//     [  0, 12], [  0, 12], [  0, 10],
 537//     [  0, 10]
 538//   ];
 539//   revolve_bezier(path, splinesteps=32, $fn=180);
 540module revolve_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER)
 541{
 542	maxx = max([for (pt = bezier) abs(pt[0])]);
 543	maxy = max([for (pt = bezier) abs(pt[1])]);
 544	orient_and_align([maxx*2,maxx*2,maxy*2], orient, align) {
 545		rotate_extrude(convexity=convexity, angle=angle) {
 546			xrot(180) zrot(-90) bezier_polygon(bezier, splinesteps, N);
 547		}
 548	}
 549}
 550
 551
 552
 553// Module: rotate_extrude_bezier()
 554// Usage:
 555//   rotate_extrude_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360)
 556// Description:
 557//   Takes a closed 2D bezier and rotates it around the Z axis, forming a solid.
 558//   Behaves like rotate_extrude(), except for beziers instead of shapes.
 559// Arguments:
 560//   bezier = array of 2D points for the bezier path to rotate.
 561//   splinesteps = number of segments to divide each bezier segment into. default=16
 562//   N = number of points in each bezier segment.  default=3 (cubic)
 563//   convexity = max number of walls a line could pass through, for preview.  default=10
 564//   angle = Degrees of sweep to make.  Default: 360
 565//   orient = Orientation of the extrusion.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
 566//   align = Alignment of the extrusion.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
 567// Example(Spin):
 568//   path = [
 569//     [  0, 10], [ 50,  0], [ 50, 40],
 570//     [ 95, 40], [100, 40], [100, 45],
 571//     [ 95, 45], [ 66, 45], [  0, 20],
 572//     [  0, 12], [  0, 12], [  0, 10],
 573//     [  0, 10]
 574//   ];
 575//   rotate_extrude_bezier(path, splinesteps=32, $fn=180);
 576module rotate_extrude_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_Z, align=V_CENTER)
 577{
 578	maxx = max([for (pt = bezier) abs(pt[0])]);
 579	maxy = max([for (pt = bezier) abs(pt[1])]);
 580	orient_and_align([maxx*2,maxx*2,0], orient, align) {
 581		rotate_extrude(convexity=convexity, angle=angle) {
 582			bezier_polygon(bezier, splinesteps, N);
 583		}
 584	}
 585}
 586
 587
 588
 589// Module: revolve_bezier_solid_to_axis()
 590// Usage:
 591//   revolve_bezier_solid_to_axis(bezier, [splinesteps], [N], [convexity], [angle], [orient], [align]);
 592// Description:
 593//   Takes a 2D bezier and rotates it around the X axis, forming a solid.
 594// Arguments:
 595//   bezier = array of points for the bezier path to rotate.
 596//   splinesteps = number of segments to divide each bezier segment into. default=16
 597//   N = number of points in each bezier segment.  default=3 (cubic)
 598//   convexity = max number of walls a line could pass through, for preview.  default=10
 599//   angle = Degrees of sweep to make.  Default: 360
 600//   orient = Orientation of the extrusion.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_X`.
 601//   align = Alignment of the extrusion.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
 602// Example(FlatSpin):
 603//   path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
 604//   revolve_bezier_solid_to_axis(path, splinesteps=32, $fn=72);
 605module revolve_bezier_solid_to_axis(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER) {
 606	revolve_bezier(bezier=bezier_close_to_axis(bezier), splinesteps=splinesteps, N=N, convexity=convexity, angle=angle, orient=orient, align=align);
 607}
 608
 609
 610// Module: revolve_bezier_offset_shell()
 611// Usage:
 612//   revolve_bezier_offset_shell(bezier, offset, [splinesteps], [N], [convexity], [angle], [orient], [align]);
 613// Description:
 614//   Takes a 2D bezier and rotates it around the X axis, into a hollow shell.
 615// Arguments:
 616//   bezier = array of points for the bezier path to rotate.
 617//   offset = the thickness of the created shell.
 618//   splinesteps = number of segments to divide each bezier segment into. default=16
 619//   N = number of points in each bezier segment.  default=3 (cubic)
 620//   convexity = max number of walls a line could pass through, for preview.  default=10
 621//   angle = degrees of sweep to make.  Default: 360
 622//   orient = Orientation of the extrusion.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_X`.
 623//   align = Alignment of the extrusion.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
 624// Example(FlatSpin):
 625//   path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
 626//   revolve_bezier_offset_shell(path, offset=1, splinesteps=32, $fn=72);
 627module revolve_bezier_offset_shell(bezier, offset=1, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER) {
 628	revolve_bezier(bezier=bezier_offset(offset, bezier), splinesteps=splinesteps, N=N, orient=orient, align=align);
 629}
 630
 631
 632// Module: extrude_2d_shapes_along_bezier()
 633// Usage:
 634//   extrude_2d_shapes_along_bezier(bezier, [splinesteps], [N], [convexity], [clipsize]) ...
 635// Description:
 636//   Extrudes 2D children along a bezier path.
 637// Arguments:
 638//   bezier = array of points for the bezier path to extrude along.
 639//   splinesteps = number of segments to divide each bezier segment into. default=16
 640// Example(FR):
 641//   path = [ [0, 0, 0], [33, 33, 33], [66, -33, -33], [100, 0, 0] ];
 642//   extrude_2d_shapes_along_bezier(path) difference(){
 643//       circle(r=10);
 644//       fwd(10/2) circle(r=8);
 645//   }
 646module extrude_2d_shapes_along_bezier(bezier, splinesteps=16, N=3, convexity=10, clipsize=1000) {
 647	path = slice(bezier_polyline(bezier, splinesteps, N), 0, -1);
 648	extrude_2d_shapes_along_3dpath(path, convexity=convexity, clipsize=clipsize) children();
 649}
 650
 651
 652// Module: extrude_bezier_along_bezier()
 653// Usage:
 654//   extrude_bezier_along_bezier(bezier, path, [pathsteps], [bezsteps], [bezN], [pathN]);
 655// Description:
 656//   Takes a closed 2D bezier path, centered on the XY plane, and
 657//   extrudes it perpendicularly along a 3D bezier path, forming a solid.
 658// Arguments:
 659//   bezier = Array of 2D points of a bezier path, to be extruded.
 660//   path = Array of 3D points of a bezier path, to extrude along.
 661//   pathsteps = number of steps to divide each path segment into.
 662//   bezsteps = number of steps to divide each bezier segment into.
 663//   bezN = number of points in each extruded bezier segment.  default=3 (cubic)
 664//   pathN = number of points in each path bezier segment.  default=3 (cubic)
 665// Example(FlatSpin):
 666//   bez = [
 667//       [-10,   0],  [-15,  -5],
 668//       [ -5, -10],  [  0, -10],  [ 5, -10],
 669//       [ 10,  -5],  [ 15,   0],  [10,   5],
 670//       [  5,  10],  [  0,  10],  [-5,  10],
 671//       [ 25, -15],  [-10,   0]
 672//   ];
 673//   path = [ [0, 0, 0], [33, 33, 33], [90, 33, -33], [100, 0, 0] ];
 674//   extrude_bezier_along_bezier(bez, path, pathsteps=32, bezsteps=16);
 675module extrude_bezier_along_bezier(bezier, path, pathsteps=16, bezsteps=16, bezN=3, pathN=3) {
 676	bez_points = simplify2d_path(bezier_polyline(bezier, bezsteps, bezN));
 677	path_points = simplify3d_path(path3d(bezier_polyline(path, pathsteps, pathN)));
 678	extrude_2dpath_along_3dpath(bez_points, path_points);
 679}
 680
 681
 682
 683// Module: trace_bezier()
 684// Description:
 685//   Renders 2D or 3D bezier paths and their associated control points.
 686//   Useful for debugging bezier paths.
 687// Arguments:
 688//   bez = the array of points in the bezier.
 689//   N = Mark the first and every Nth vertex after in a different color and shape.
 690//   size = diameter of the lines drawn.
 691// Example(2D):
 692//   bez = [
 693//       [-10,   0],  [-15,  -5],
 694//       [ -5, -10],  [  0, -10],  [ 5, -10],
 695//       [ 14,  -5],  [ 15,   0],  [16,   5],
 696//       [  5,  10],  [  0,  10]
 697//   ];
 698//   trace_bezier(bez, N=3, size=0.5);
 699module trace_bezier(bez, N=3, size=1) {
 700	trace_polyline(bez, N=N, showpts=true, size=size/2, color="green");
 701	trace_polyline(bezier_polyline(bez, N=N), size=size, color="cyan");
 702}
 703
 704
 705
 706// Section: Patch Functions
 707
 708
 709// Function: bezier_patch_point()
 710// Usage:
 711//   bezier_patch_point(patch, u, v)
 712// Description:
 713//   Given a square 2-dimensional array of (N+1) by (N+1) points size,
 714//   that represents a Bezier Patch of degree N, returns a point on that
 715//   surface, at positions `u`, and `v`.  A cubic bezier patch will be 4x4
 716//   points in size.  If given a non-square array, each direction will have
 717//   its own degree.
 718// Arguments:
 719//   patch = The 2D array of endpoints and control points for this bezier patch.
 720//   u = The proportion of the way along the first dimension of the patch to find the point of.  0<=`u`<=1
 721//   v = The proportion of the way along the second dimension of the patch to find the point of.  0<=`v`<=1
 722// Example(3D):
 723//   patch = [
 724//       [[-50, 50,  0], [-16, 50,  20], [ 16, 50,  20], [50, 50,  0]],
 725//       [[-50, 16, 20], [-16, 16,  40], [ 16, 16,  40], [50, 16, 20]],
 726//       [[-50,-16, 20], [-16,-16,  40], [ 16,-16,  40], [50,-16, 20]],
 727//       [[-50,-50,  0], [-16,-50,  20], [ 16,-50,  20], [50,-50,  0]]
 728//   ];
 729//   trace_bezier_patches(patches=[patch], size=1, showcps=true);
 730//   pt = bezier_patch_point(patch, 0.6, 0.75);
 731//   translate(pt) color("magenta") sphere(d=3, $fn=12);
 732function bezier_patch_point(patch, u, v) = bez_point([for (bez = patch) bez_point(bez, u)], v);
 733
 734
 735// Function: bezier_triangle_point()
 736// Usage:
 737//   bezier_triangle_point(tri, u, v)
 738// Description:
 739//   Given a triangular 2-dimensional array of N+1 by (for the first row) N+1 points,
 740//   that represents a Bezier triangular patch of degree N, returns a point on
 741//   that surface, at positions `u`, and `v`.  A cubic bezier triangular patch
 742//   will have a list of 4 points in the first row, 3 in the second, 2 in the
 743//   third, and 1 in the last row.
 744// Arguments:
 745//   tri = Triangular bezier patch to get point on.
 746//   u = The proportion of the way along the first dimension of the triangular patch to find the point of.  0<=`u`<=1
 747//   v = The proportion of the way along the second dimension of the triangular patch to find the point of.  0<=`v`<=(1-`u`)
 748// Example(3D):
 749//   tri = [
 750//       [[-50,-33,0], [-25,16,40], [20,66,20]],
 751//       [[0,-33,30], [25,16,30]],
 752//       [[50,-33,0]]
 753//   ];
 754//   trace_bezier_patches(tris=[tri], size=1, showcps=true);
 755//   pt = bezier_triangle_point(tri, 0.5, 0.2);
 756//   translate(pt) color("magenta") sphere(d=3, $fn=12);
 757function bezier_triangle_point(tri, u, v) =
 758	len(tri) == 1 ? tri[0][0] :
 759	let(
 760		n = len(tri)-1,
 761		Pu = [for(i=[0:n-1]) [for (j=[1:len(tri[i])-1]) tri[i][j]]],
 762		Pv = [for(i=[0:n-1]) [for (j=[0:len(tri[i])-2]) tri[i][j]]],
 763		Pw = [for(i=[1:len(tri)-1]) tri[i]]
 764	)
 765	bezier_triangle_point(u*Pu + v*Pv + (1-u-v)*Pw, u, v);
 766
 767
 768
 769// Function: bezier_patch()
 770// Usage:
 771//   bezier_patch(patch, [splinesteps], [vertices], [faces]);
 772// Description:
 773//   Calculate vertices and faces for forming a partial polyhedron
 774//   from the given bezier rectangular patch.  Returns a list containing
 775//   two elements.  The first is the list of unique vertices.  The
 776//   second is the list of faces, where each face is a list of indices
 777//   into the list of vertices.  You can chain calls to this, to add
 778//   more vertices and faces for multiple bezier patches, to stitch
 779//   them together into a complete polyhedron.
 780// Arguments:
 781//   patch = The rectangular array of endpoints and control points for this bezier patch.
 782//   splinesteps = Number of steps to divide each bezier segment into.  Default: 16
 783//   vertices = Vertex list to add new points to.  Default: []
 784//   faces = Face list to add new faces to.  Default: []
 785// Example(3D):
 786//   patch = [
 787//       [[-50, 50,  0], [-16, 50, -20], [ 16, 50,  20], [50, 50,  0]],
 788//       [[-50, 16, 20], [-16, 16, -20], [ 16, 16,  20], [50, 16, 20]],
 789//       [[-50,-16, 20], [-16,-16,  20], [ 16,-16, -20], [50,-16, 20]],
 790//       [[-50,-50,  0], [-16,-50,  20], [ 16,-50, -20], [50,-50,  0]]
 791//   ];
 792//   vnf = bezier_patch(patch, splinesteps=16);
 793//   polyhedron(points=vnf[0], faces=vnf[1]);
 794function bezier_patch(patch, splinesteps=16, vertices=[], faces=[]) =
 795	let(
 796		base = len(vertices),
 797		pts = [for (v=[0:splinesteps], u=[0:splinesteps]) bezier_patch_point(patch, u/splinesteps, v/splinesteps)],
 798		new_vertices = concat(vertices, pts),
 799		new_faces = [
 800			for (
 801				v=[0:splinesteps-1],
 802				u=[0:splinesteps-1],
 803				i=[0,1]
 804			) let (
 805				v1 = u+v*(splinesteps+1) + base,
 806				v2 = v1 + 1,
 807				v3 = v1 + splinesteps + 1,
 808				v4 = v3 + 1,
 809				face = i? [v1,v3,v2] : [v2,v3,v4]
 810			) face
 811		]
 812	) [new_vertices, concat(faces, new_faces)];
 813
 814
 815function _tri_count(n) = (n*(1+n))/2;
 816
 817
 818// Function: bezier_triangle()
 819// Usage:
 820//   bezier_triangle(tri, [splinesteps], [vertices], [faces]);
 821// Description:
 822//   Calculate vertices and faces for forming a partial polyhedron
 823//   from the given bezier triangular patch.  Returns a list containing
 824//   two elements.  The first is the list of unique vertices.  The
 825//   second is the list of faces, where each face is a list of indices
 826//   into the list of vertices.  You can chain calls to this, to add
 827//   more vertices and faces for multiple bezier patches, to stitch
 828//   them together into a complete polyhedron.
 829// Arguments:
 830//   tri = The triangular array of endpoints and control points for this bezier patch.
 831//   splinesteps = Number of steps to divide each bezier segment into.  Default: 16
 832//   vertices = Vertex list to add new points to.  Default: []
 833//   faces = Face list to add new faces to.  Default: []
 834// Example(3D):
 835//   tri = [
 836//       [[-50,-33,0], [-25,16,50], [0,66,0]],
 837//       [[0,-33,50], [25,16,50]],
 838//       [[50,-33,0]]
 839//   ];
 840//   vnf = bezier_triangle(tri, splinesteps=16);
 841//   polyhedron(points=vnf[0], faces=vnf[1]);
 842function bezier_triangle(tri, splinesteps=16, vertices=[], faces=[]) =
 843	let(
 844		base = len(vertices),
 845		pts = [
 846			for (
 847				u=[0:splinesteps],
 848				v=[0:splinesteps-u]
 849			) bezier_triangle_point(tri, u/splinesteps, v/splinesteps)
 850		],
 851		new_vertices = concat(vertices, pts),
 852		patchlen = len(tri),
 853		tricnt = _tri_count(splinesteps+1),
 854		new_faces = [
 855			for (
 856				u=[0:splinesteps-1],
 857				v=[0:splinesteps-u-1]
 858			) let (
 859				v1 = v + (tricnt - _tri_count(splinesteps+1-u)) + base,
 860				v2 = v1 + 1,
 861				v3 = v + (tricnt - _tri_count(splinesteps-u)) + base,
 862				v4 = v3 + 1,
 863				allfaces = concat(
 864					[[v1,v2,v3]],
 865					((u<splinesteps-1 && v<splinesteps-u-1)? [[v2,v4,v3]] : [])
 866				)
 867			)  for (face=allfaces) face
 868		]
 869	) [new_vertices, concat(faces, new_faces)];
 870
 871
 872
 873// Function: bezier_patch_flat()
 874// Usage:
 875//   bezier_patch_flat(size, [N], [orient], [trans]);
 876// Description:
 877//   Returns a flat rectangular bezier patch of degree `N`, centered on the XY plane.
 878// Arguments:
 879//   size = 2D XY size of the patch.
 880//   N = Degree of the patch to generate.  Since this is flat, a degree of 1 should usually be sufficient.
 881//   orient = The orientation to rotate the edge patch into.  Use the `ORIENT` constants in `BOSL/constants.scad`.
 882//   trans = Amount to translate patch, after rotating to `orient`.
 883// Example(3D):
 884//   patch = bezier_patch_flat(size=[100,100], N=3);
 885//   trace_bezier_patches([patch], size=1, showcps=true);
 886function bezier_patch_flat(size=[100,100], N=4, orient=ORIENT_Z, trans=[0,0,0]) =
 887	let(
 888		patch = [for (x=[0:N]) [for (y=[0:N]) vmul(point3d(size),[x/N-0.5, 0.5-y/N, 0])]]
 889	) [for (row=patch)
 890		translate_points(v=trans,
 891			rotate_points3d(v=orient,row)
 892		)
 893	];
 894
 895
 896
 897// Function: patch_reverse()
 898// Usage:
 899//   patch_reverse(patch)
 900// Description:
 901//   Reverses the patch, so that the faces generated from it are flipped back to front.
 902// Arguments:
 903//   patch = The patch to reverse.
 904function patch_reverse(patch) = [for (row=patch) reverse(row)];
 905
 906
 907
 908// Function: patch_translate()
 909// Usage:
 910//   patch_translate(patch, v)
 911// Description: Translates all coordinates in a rectangular or triangular patch by a given amount.
 912// Arguments:
 913//   patch = The patch to translate.
 914//   v = Vector to translate by.
 915function patch_translate(patch, v=[0,0,0]) = [for(row=patch) translate_points(row, v)];
 916
 917
 918// Function: patch_scale()
 919// Usage:
 920//   patch_scale(patch, v, [cp])
 921// Description: Scales all coordinates in a rectangular or triangular patch by a given amount.
 922// Arguments:
 923//   patch = The patch to scale.
 924//   v = [X,Y,Z] scaling factors.
 925//   cp = Centerpoint to scale around.
 926function patch_scale(patch, v=[1,1,1], cp=[0,0,0]) = [for(row=patch) scale_points(row, v, cp)];
 927
 928
 929// Function: patch_rotate()
 930// Usage:
 931//   patch_rotate(patch, a, [cp])
 932//   patch_rotate(patch, a, v, [cp])
 933// Description: Rotates all coordinates in a rectangular or triangular patch by a given amount.
 934// Arguments:
 935//   patch = The patch to rotate.
 936//   a = Rotation angle(s) in degrees.
 937//   v = Vector axis to rotate round.
 938//   cp = Centerpoint to rotate around.
 939function patch_rotate(patch, a=undef, v=undef, cp=[0,0,0]) =
 940	v==undef?
 941		[for(row=patch) rotate_points3d(row, a, cp)] :
 942		[for(row=patch) rotate_points3d_around_axis(row, a, v, cp)];
 943
 944
 945// Function: patches_translate()
 946// Usage:
 947//   patches_translate(patch, v, [cp])
 948// Description: Translates all coordinates in each of a list of rectangular or triangular patches.
 949// Arguments:
 950//   patches = List of patches to translate.
 951//   v = Vector to translate by.
 952function patches_translate(patches, v=[0,0,0]) = [for (patch=patches) patch_translate(patch,v)];
 953
 954
 955// Function: patches_scale()
 956// Usage:
 957//   patches_scale(patch, v, [cp])
 958// Description: Scales all coordinates in each of a list of rectangular or triangular patches.
 959// Arguments:
 960//   patches = List of patches to scale.
 961//   v = [X,Y,Z] scaling factors.
 962//   cp = Centerpoint to scale around.
 963function patches_scale(patches, v=[1,1,1], cp=[0,0,0]) = [for (patch=patches) patch_scale(patch,v,cp)];
 964
 965
 966// Function: patches_rotate()
 967// Usage:
 968//   patches_rotate(patch, a, [cp])
 969//   patches_rotate(patch, a, v, [cp])
 970// Description: Rotates all coordinates in each of a list of rectangular or triangular patches.
 971// Arguments:
 972//   patches = List of patches to rotate.
 973//   a = Rotation angle(s) in degrees.
 974//   v = Vector axis to rotate round.
 975//   cp = Centerpoint to rotate around.
 976function patches_rotate(patches, a=undef, v=undef, cp=[0,0,0]) = [for (patch=patches) patch_rotate(patch, a=a, v=v, cp=cp)];
 977
 978
 979// Function: bezier_surface()
 980// Usage:
 981//   bezier_surface(patches, [splinesteps], [vertices], [faces]);
 982// Description:
 983//   Calculate vertices and faces for forming a (possibly partial)
 984//   polyhedron from the given rectangular and triangular bezier
 985//   patches.  Returns a list containing two elements.  The first is
 986//   the list of unique vertices.  The second is the list of faces,
 987//   where each face is a list of indices into the list of vertices.
 988//   You can chain calls to this, to add more vertices and faces for
 989//   multiple bezier patches, to stitch them together into a complete
 990//   polyhedron.
 991// Arguments:
 992//   patches = A list of rectangular bezier patches.
 993//   tris = A list of triangular bezier patches.
 994//   splinesteps = Number of steps to divide each bezier segment into.  Default: 16
 995//   vertices = Vertex list to add new points to.  Default: []
 996//   faces = Face list to add new faces to.  Default: []
 997// Example(3D):
 998//   patch1 = [
 999//   	[[18,18,0], [33,  0,  0], [ 67,  0,  0], [ 82, 18,0]],
1000//   	[[ 0,40,0], [ 0,  0,100], [100,  0, 20], [100, 40,0]],
1001//   	[[ 0,60,0], [ 0,100,100], [100,100, 20], [100, 60,0]],
1002//   	[[18,82,0], [33,100,  0], [ 67,100,  0], [ 82, 82,0]],
1003//   ];
1004//   patch2 = [
1005//   	[[18,18,0], [33,  0,  0], [ 67,  0,  0], [ 82, 18,0]],
1006//   	[[ 0,40,0], [ 0,  0,-50], [100,  0,-50], [100, 40,0]],
1007//   	[[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
1008//   	[[18,82,0], [33,100,  0], [ 67,100,  0], [ 82, 82,0]],
1009//   ];
1010//   vnf = bezier_surface(patches=[patch1, patch2], splinesteps=16);
1011//   polyhedron(points=vnf[0], faces=vnf[1]);
1012function bezier_surface(patches=[], tris=[], splinesteps=16, i=0, vertices=[], faces=[]) =
1013	let(
1014		vnf = (i >= len(patches))? [vertices, faces] :
1015			bezier_patch(patches[i], splinesteps=splinesteps, vertices=vertices, faces=faces),
1016		vnf2 = (i >= len(tris))? vnf :
1017			bezier_triangle(tris[i], splinesteps=splinesteps, vertices=vnf[0], faces=vnf[1])
1018	) (i >= len(patches) && i >= len(tris))? vnf2 :
1019	bezier_surface(patches=patches, tris=tris, splinesteps=splinesteps, i=i+1, vertices=vnf2[0], faces=vnf2[1]);
1020
1021
1022
1023// Section: Bezier Surface Modules
1024
1025
1026// Module: bezier_polyhedron()
1027// Useage:
1028//   bezier_polyhedron(patches)
1029// Description:
1030//   Takes a list of two or more bezier patches and attempts to make a complete polyhedron from them.
1031// Arguments:
1032//   patches = A list of rectangular bezier patches.
1033//   tris = A list of triangular bezier patches.
1034//   vertices = Vertex list for additional non-bezier faces.  Default: []
1035//   faces = Additional non-bezier faces.  Default: []
1036//   splinesteps = Number of steps to divide each bezier segment into. Default: 16
1037// Example:
1038//   patch1 = [
1039//   	[[18,18,0], [33,  0,  0], [ 67,  0,  0], [ 82, 18,0]],
1040//   	[[ 0,40,0], [ 0,  0, 20], [100,  0, 20], [100, 40,0]],
1041//   	[[ 0,60,0], [ 0,100, 20], [100,100,100], [100, 60,0]],
1042//   	[[18,82,0], [33,100,  0], [ 67,100,  0], [ 82, 82,0]],
1043//   ];
1044//   patch2 = [
1045//   	[[18,18,0], [33,  0,  0], [ 67,  0,  0], [ 82, 18,0]],
1046//   	[[ 0,40,0], [ 0,  0,-50], [100,  0,-50], [100, 40,0]],
1047//   	[[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
1048//   	[[18,82,0], [33,100,  0], [ 67,100,  0], [ 82, 82,0]],
1049//   ];
1050//   bezier_polyhedron([patch1, patch2], splinesteps=8);
1051module bezier_polyhedron(patches=[], tris=[], splinesteps=16, vertices=[], faces=[])
1052{
1053	sfc = bezier_surface(patches=patches, tris=tris, splinesteps=splinesteps, vertices=vertices, faces=faces);
1054	polyhedron(points=sfc[0], faces=sfc[1]);
1055}
1056
1057
1058
1059// Module: trace_bezier_patches()
1060// Usage:
1061//   trace_bezier_patches(patches, [size], [showcps], [splinesteps]);
1062//   trace_bezier_patches(tris, [size], [showcps], [splinesteps]);
1063//   trace_bezier_patches(patches, tris, [size], [showcps], [splinesteps]);
1064// Description:
1065//   Shows the surface, and optionally, control points of a list of bezier patches.
1066// Arguments:
1067//   patches = A list of rectangular bezier patches.
1068//   tris = A list of triangular bezier patches.
1069//   splinesteps = Number of steps to divide each bezier segment into. default=16
1070//   showcps = If true, show the controlpoints as well as the surface.
1071//   size = Size to show control points and lines.
1072// Example:
1073//   patch1 = [
1074//   	[[15,15,0], [33,  0,  0], [ 67,  0,  0], [ 85, 15,0]],
1075//   	[[ 0,33,0], [33, 33, 50], [ 67, 33, 50], [100, 33,0]],
1076//   	[[ 0,67,0], [33, 67, 50], [ 67, 67, 50], [100, 67,0]],
1077//   	[[15,85,0], [33,100,  0], [ 67,100,  0], [ 85, 85,0]],
1078//   ];
1079//   patch2 = [
1080//   	[[15,15,0], [33,  0,  0], [ 67,  0,  0], [ 85, 15,0]],
1081//   	[[ 0,33,0], [33, 33,-50], [ 67, 33,-50], [100, 33,0]],
1082//   	[[ 0,67,0], [33, 67,-50], [ 67, 67,-50], [100, 67,0]],
1083//   	[[15,85,0], [33,100,  0], [ 67,100,  0], [ 85, 85,0]],
1084//   ];
1085//   trace_bezier_patches(patches=[patch1, patch2], splinesteps=8, showcps=true);
1086module trace_bezier_patches(patches=[], tris=[], size=1, showcps=false, splinesteps=16)
1087{
1088	if (showcps) {
1089		for (patch = patches) {
1090			place_copies(flatten(patch)) color("red") sphere(d=size*2);
1091			color("cyan")
1092			for (i=[0:len(patch)-1], j=[0:len(patch[i])-1]) {
1093				if (i<len(patch)-1) extrude_from_to(patch[i][j], patch[i+1][j]) circle(d=size);
1094				if (j<len(patch[i])-1) extrude_from_to(patch[i][j], patch[i][j+1]) circle(d=size);
1095			}
1096			vnf = bezier_patch(patch, splinesteps=splinesteps);
1097			color("blue") place_copies(vnf[0]) sphere(d=size);
1098		}
1099		for (patch = tris) {
1100			place_copies(flatten(patch)) color("red") sphere(d=size*2);
1101			color("cyan")
1102			for (i=[0:len(patch)-2], j=[0:len(patch[i])-2]) {
1103				extrude_from_to(patch[i][j], patch[i+1][j]) circle(d=size);
1104				extrude_from_to(patch[i][j], patch[i][j+1]) circle(d=size);
1105				extrude_from_to(patch[i+1][j], patch[i][j+1]) circle(d=size);
1106			}
1107			vnf = bezier_triangle(patch, splinesteps=splinesteps);
1108			color("blue") place_copies(vnf[0]) sphere(d=size);
1109		}
1110	}
1111	bezier_polyhedron(patches=patches, tris=tris, splinesteps=splinesteps);
1112}
1113
1114
1115
1116// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap